package svc

import (
	"cloud-entropy-storage-system/common/pkg/msg"
	"cloud-entropy-storage-system/common/pkg/session"
	"cloud-entropy-storage-system/server/internal/types/form"
	"cloud-entropy-storage-system/server/model/user"
	"encoding/json"
	"errors"
	"github.com/bluele/gcache"
	"github.com/cilidm/toolbox/jwt"
	"sync/atomic"
	"time"

	"github.com/asdine/storm/v3"
	"github.com/cilidm/toolbox/str"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

type UserSvc struct {
	log     *zap.Logger
	operate *user.Operate
	gc      gcache.Cache
}

func NewUserSvc(log *zap.Logger, db *storm.DB, cache gcache.Cache) *UserSvc {
	return &UserSvc{log: log, operate: user.NewUserData(db), gc: cache}
}

func (svc *UserSvc) ChangePwd(uid int, f form.Password) error {
	u, err := svc.operate.FindById(uid)
	if err != nil {
		svc.log.Error("svc.operate.FindById", zap.Error(err))
		return err
	}
	if u.Password != str.Md5([]byte(f.OldPwd+u.Salt)) {
		return errors.New(msg.GetMsg(msg.ErrorPwd))
	}
	pwd, salt := NewPassword(10, f.NewPwd)
	return svc.operate.Update(user.User{ID: u.ID, Password: pwd, Salt: salt})
}

// true:ip已受限  false:未限制
func (svc *UserSvc) CheckIp(ip string) bool {
	val, has := svc.hasIP(ip)
	if !has {
		return false
	}
	if val >= 5 {
		return true
	}
	return false
}

func (svc *UserSvc) hasIP(ip string) (int64, bool) {
	val, err := svc.gc.Get(ip)
	if err != nil && err == gcache.KeyNotFoundError {
		return 0, false
	}
	return val.(int64), true
}

func (svc *UserSvc) AddIPCount(ip string) {
	var num int64
	val, err := svc.gc.Get(ip)
	if err != nil && err == gcache.KeyNotFoundError {
		num = 0
	} else {
		num = val.(int64)
	}
	atomic.AddInt64(&num, 1)
	if err = svc.gc.SetWithExpire(ip, num, time.Minute*5); err != nil {
		svc.log.Error("svc.gc.SetWithExpire", zap.Error(err))
	}
}

func (svc *UserSvc) Login(username, password string, ctx *gin.Context) (string, int) {
	u, err := svc.operate.FindUserByName(username)
	if err != nil {
		svc.log.Error("svc.operate.FindUserByName", zap.Error(err))
		return "", msg.ERROR
	}
	if u == nil {
		return "", msg.ErrorLoginCheckUser
	}
	if u.Password != str.Md5([]byte(password+u.Salt)) {
		return "", msg.ErrorLoginCheckPwd
	}
	if u.Status == 2 {
		return "", msg.ErrorLoginCheckStatus
	}
	if err = svc.operate.Update(user.User{ID: u.ID, LastLoginIp: ctx.ClientIP(),
		LastLoginTime: time.Now()}); err != nil {
		svc.log.Error("svc.uData.Update", zap.Error(err))
		return "", msg.ErrorUpdate
	}
	if err = svc.saveSession(u, ctx); err != nil {
		svc.log.Error("svc.saveSession", zap.Error(err))
		return "", msg.ErrorSetSession
	}
	token, err := jwt.NewClaims(u.ID, u.Username).Generate()
	if err != nil {
		svc.log.Error("Generate", zap.Error(err))
		return "", msg.ERROR
	}
	return token, msg.SUCCESS
}

func (svc *UserSvc) saveSession(u *user.User, ctx *gin.Context) error {
	err := session.Set(ctx, user.Uid, u.ID)
	if err != nil {
		svc.log.Error("session.Set", zap.Error(err))
		return err
	}
	tmp, _ := json.Marshal(u)
	err = session.Set(ctx, user.SessionInfo, string(tmp))
	if err != nil {
		svc.log.Error("session.Set", zap.Error(err))
		return err
	}
	return nil
}
